package tianrun.ziguan.api.calculate.config.service.redis.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import tianrun.ziguan.api.calculate.config.config.redis.JedisExecutor;
import tianrun.ziguan.api.calculate.config.service.redis.IRedisService;
import tianrun.ziguan.api.common.exception.RedisConnectException;

import java.io.IOException;
import java.util.*;

/**
 * Redis 工具类，只封装了几个常用的 redis 命令，
 * 可根据实际需要按类似的方式扩展即可。
 *
 * @author MrBird
 */
@Service
public class RedisServiceImpl implements IRedisService {
    private static final Logger log = LoggerFactory.getLogger(RedisServiceImpl.class);

    private final JedisPool jedisPool;

    public RedisServiceImpl(@Autowired(required = false) JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 处理 jedis请求
     *
     * @param j 处理逻辑，通过 lambda行为参数化
     * @return 处理结果
     */
    private <T> T executorByJedis(JedisExecutor<Jedis, T> j) throws RedisConnectException {
        try (Jedis jedis = jedisPool.getResource()) {
            return j.executor(jedis);
        }
    }

    /**
     * 处理 jedis请求
     *
     * @param j 处理逻辑，通过 lambda行为参数化
     * @return 处理结果
     */
    private <T> T executorByJedis(int index, JedisExecutor<Jedis, T> j) throws RedisConnectException {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.select(index);
            return j.executor(jedis);
        }
    }


    @Override
    public Set<String> getKeys(String pattern) throws RedisConnectException {
        return this.executorByJedis(j -> j.keys(pattern));
    }

    @Override
    public String get(String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.get(key));
    }

    @Override
    public String get(int index, String key) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.get(key));
    }

    @Override
    public List<String> mGet(String[] keys) throws RedisConnectException {
        return executorByJedis(k -> k.mget(keys));
    }

    @Override
    public String set(String key, String value) throws RedisConnectException {
        return this.executorByJedis(j -> j.set(key, value));
    }

    @Override
    public String set(int index, String key, String value) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.set(key, value));
    }

    @Override
    public String set(String key, String value, Long milliscends) throws RedisConnectException {
        String result = this.set(key, value);
        this.pexpire(key, milliscends);
        return result;
    }

    @Override
    public String set(int index, String key, String value, Long milliscends) throws RedisConnectException {
        String result = this.set(index,key, value);
        this.pexpire(index,key, milliscends);
        return result;
    }

    @Override
    public Long del(String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.del(key));
    }

    @Override
    public Long del(int index, String key) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.del(key));
    }

    @Override
    public Boolean exists(String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.exists(key));
    }

    @Override
    public Long pttl(String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.pttl(key));
    }

    @Override
    public Long pexpire(String key, Long milliseconds) throws RedisConnectException {
        return this.executorByJedis(j -> j.pexpire(key, milliseconds));
    }
    public Long pexpire(int index,String key, Long milliseconds) throws RedisConnectException {
        return this.executorByJedis(index,j -> j.pexpire(key, milliseconds));
    }

    @Override
    public Long zadd(String key, Double score, String member) throws RedisConnectException {
        return this.executorByJedis(j -> j.zadd(key, score, member));
    }

    @Override
    public Set<String> zrangeByScore(String key, String min, String max) throws RedisConnectException {
        return this.executorByJedis(j -> j.zrangeByScore(key, min, max));
    }

    @Override
    public Long zremrangeByScore(String key, String start, String end) throws RedisConnectException {
        return this.executorByJedis(j -> j.zremrangeByScore(key, start, end));
    }

    @Override
    public Long zrem(String key, String... members) throws RedisConnectException {
        return this.executorByJedis(j -> j.zrem(key, members));
    }

    @Override
    public List<String> getList(String key, int start, int end) throws RedisConnectException {
        return this.executorByJedis(j -> j.lrange(key, 0, end));
    }

    @Override
    public List<String> getList(String key, int start) throws RedisConnectException {
        return this.executorByJedis(j -> j.lrange(key, 0, j.llen(key)));
    }

    @Override
    public void setValue(String key, String... value) throws RedisConnectException {
        this.executorByJedis(j -> j.lpush(key, value));
    }

    @Override
    public void rPush(String key, String... values) throws RedisConnectException {
        this.executorByJedis(j -> j.rpush(key, values));
    }

    @Override
    public List<Object> pipelineGetValue(Collection<String> keyList) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();

        try {
            for (String key : keyList) {
                if (StringUtils.isNotEmpty(key)) {
                    p.get(key);
                }
            }
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public List<Object> pipelineLRange(Collection<String> keyList) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();

        try {
            for (String key : keyList) {
                if (StringUtils.isNotEmpty(key)) {
                    p.lrange(key, 0, -1);
                }
            }
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public String lindex(String key, long i) throws RedisConnectException {
        return this.executorByJedis(j -> j.lindex(key, i));
    }

    @Override
    public List<Object> pipelineLIndex(Collection<String> keyList, long i) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();

        try {
            for (String key : keyList) {
                if (StringUtils.isNotEmpty(key)) {
                    p.lindex(key, i);
                }
            }
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public String mSet(String[] kvs) throws RedisConnectException {
        if (kvs == null || kvs.length == 0) {
            return null;
        }
        return this.executorByJedis(j -> j.mset(kvs));
    }

    @Override
    public List<String> getListIngoreCase(String key, int i) throws RedisConnectException {
        return this.executorByJedis(j -> j.lrange(key, 0, j.llen(key)));
    }

    @Override
    public Long hmSet(String tableKey, String key, String value) throws RedisConnectException {
        return this.executorByJedis(j -> j.hset(tableKey, key, value));
    }

    @Override
    public Long hmSet(int index, String tableKey, String key, String value) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.hset(tableKey, key, value));
    }

    @Override
    public boolean hmSet(int index, String key, Map<String, String> value) {
        String res;
        try {
            res = this.executorByJedis(index, j -> j.hmset(key, value));
        } catch (RedisConnectException e) {
            log.error("redis hmset index: {}; key: {},  error", index, key, e);
            return false;
        }
        return "OK".equals(res);
    }
    @Override
    public boolean hmSet(String key, Map<String, String> value) {
        String res;
        try {
            res = this.executorByJedis(j -> j.hmset(key, value));
        } catch (RedisConnectException e) {
            log.error("redis hmset index: {}; key: {},  error", key, e);
            return false;
        }
        return "OK".equals(res);
    }

    @Override
    public List<Object> pipelineHSet(String key, Map<String, String> data) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();

        try {
            data.forEach((hkey, value) -> p.hset(key, hkey, value));
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public List<Object> pipelineHSet(int index, String key, Map<String, String> data) {
        Jedis jedis = jedisPool.getResource();
        jedis.select(index);
        Pipeline p = jedis.pipelined();

        try {
            data.forEach((hkey, value) -> p.hset(key, hkey, value));
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }
    @Override
    public List<Object> pipelineHSet(int index, String key, Map<String, String> data,int seconds) {
        Jedis jedis = jedisPool.getResource();
        jedis.select(index);
        Pipeline p = jedis.pipelined();

        try {
            data.forEach((hkey, value) -> p.hset(key, hkey, value));
            p.expire(key,seconds);
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public List<Object> pipelineHGet(String key, Set<String> mapKeys) {
        try (Jedis jedis = jedisPool.getResource();
             Pipeline p = jedis.pipelined()) {
            for (String hkey : mapKeys) {
                p.hget(key, hkey);
            }
            return p.syncAndReturnAll();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    @Override
    public List<Object> pipelineHGet(int index, String key, Set<String> mapKeys) {
        Jedis jedis = jedisPool.getResource();
        jedis.select(index);
        Pipeline p = jedis.pipelined();

        try {
            for (String hkey : mapKeys) {
                p.hget(key, hkey);
            }
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public String hGet(String tableKey, String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.hget(tableKey, key));
    }
    @Override
    public Map<String, String> hGetAll(String tableKey) throws RedisConnectException {
        return this.executorByJedis(j -> j.hgetAll(tableKey));
    }
    @Override
    public Map<String, String> hGetAll(int index,String tableKey) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.hgetAll(tableKey));
    }

    @Override
    public String hGet(int index, String tableKey, String key) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.hget(tableKey, key));
    }
    public List<Object> hGet(int index, String tableKey, Set<String> keys) {
        Jedis jedis = jedisPool.getResource();
        jedis.select(index);
        Pipeline p = jedis.pipelined();

        try {
            for (String hkey : keys) {
                p.hget(tableKey, hkey);
            }
            return p.syncAndReturnAll();
        } finally {
            jedis.close();
        }
    }

    @Override
    public Boolean hExists(String tableKey, String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.hexists(tableKey, key));
    }

    @Override
    public Long hdel(String tableKey, String key) throws RedisConnectException {
        return this.executorByJedis(j -> j.hdel(tableKey, key));
    }

    @Override
    public Long hdel(int index, String tableKey, String key) throws RedisConnectException {
        return this.executorByJedis(index, j -> j.hdel(tableKey, key));

    }

    @Override
    public boolean setnx(String key, String value, int seconds) throws RedisConnectException {
        Long result = this.executorByJedis(j -> j.setnx(key, value));
        if (new Long(1L).equals(result)) {
            // 设定过期时间，最多30秒自动过期，防止长期死锁发生
            this.executorByJedis(j -> j.expire(key, seconds));
            return true;
        }
        return false;
    }
}
